package com.mathdroid.model.talents;

import java.io.Serializable;
import java.util.LinkedList;

import com.mathdroid.model.Model;
import com.mathdroid.model.enums.ETalentTree;

/**
 * A talent that can require an other talent.
 */
public class Talent implements Serializable {

	/**
	 * Generated UID.
	 */
	private static final long serialVersionUID = -6031801621906203769L;

	/**
	 * The name of this talent.
	 */
	private String name = null;
	
	/**
	 * The descriptions of this talent for each stage.
	 */
	private LinkedList<String> descriptions = null;
	
	/**
	 * The required talent, to put points in this talent.
	 */
	private Talent requiredTalent = null;
	
	/**
	 * The must have amount of points in the required talent.
	 */
	private int requiredTalentPoints = 0;
	
	/**
	 * The required points in the whole talent tree.
	 */
	private int requiredTotalPoints = 0;
	
	/**
	 * The position in the GUI.
	 */
	private int guiSlot = 0;
	
	/**
	 * The points put into this talent.
	 */
	private int points = 0;
	
	/**
	 * The tree this talent belongs to.
	 */
	private ETalentTree tree = null;
	
	/**
	 * The name of the image icon.
	 */
	private String image = null;

	/**
	 * Talent constructor.
	 * @param n The name of the talent.
	 * @param d The description for each stage.
	 * @param reqT The required talent.
	 * @param req Points needed in the required talent.
	 * @param reqTotal Points required in the talents tree.
	 * @param gui The slot id of the user interface.
	 * @param p The points in this talent.
	 * @param t The talent tree this talent belongs to.
	 * @param img The name of the image icon without extension.
	 */
	public Talent(
			final String n, final LinkedList<String> d, 
			final Talent reqT, final int req, 
			final int reqTotal, final int gui,
			final int p, 
			final ETalentTree t, final String img) {
		this.name = n;
		this.descriptions = d;
		this.requiredTalent = reqT;
		this.requiredTalentPoints = req;
		this.requiredTotalPoints = reqTotal;
		this.guiSlot = gui;
		this.points = p;
		this.tree = t;
		this.image = img;
	}
	
	/**
	 * Get the required talent.
	 * @return Required Talent.
	 */
	public final Talent getRequiredTalent() {
		return requiredTalent;
	}
	
	/**
	 * Get the required talent points in the required talent.
	 * @return Required points in required talent.
	 */
	public final int getRequiredTalentPoints() {
		return requiredTalentPoints;
	}
	
	/**
	 * Get the required points in the tree.
	 * @return Required points in the tree.
	 */
	public final int getRequiredTotalPoints() {
		return requiredTotalPoints;
	}
	
	/**
	 * Get the slot id in the UI.
	 * @return Slot ID.
	 */
	public final int getGuiSlot() {
		return guiSlot;
	}

	/**
	 * Get the points in this talent.
	 * @return Talent points.
	 */
	public final int getPoints() {
		return points;
	}

	/**
	 * Get the maximum possible points in this talent.
	 * @return Maximum possible points.
	 */
	public final int getMaxPoints() {
		return this.descriptions.size();
	}

	/**
	 * Get the name of this talent.
	 * @return Name.
	 */
	public final String getName() {
		return name;
	}
	
	/**
	 * Get the descriptions for each talent level.
	 * @return Descrptions.
	 */
	public final LinkedList<String> getDescriptions() {
		return descriptions;
	}
	
	/**
	 * Get the talent tree enumeration of this talent.
	 * @return Talent tree enumeration.
	 */
	public final ETalentTree getTree() {
		return tree;
	}

	/**
	 * Get the icon of this talent.
	 * @return Icon.
	 */
	public final String getImage() {
		return image;
	}
	
	/**
	 * Check if a increase in points is possible.
	 * @return Increase is possible.
	 */
	public final boolean isIncreasePossible() {
		boolean valid = true;
		
		if (this.points >= this.descriptions.size()) {
			valid = false;
		}
		
		if (this.requiredTalent != null 
				&& this.requiredTalent.points 
					< this.requiredTalentPoints) {
			valid = false;
		}
		
		if (this.requiredTotalPoints 
					> Model.getProfile().getSpecTemplate().getTreePoints(
							this.tree)) {
			valid = false;
		}
		
		if (Model.getProfile().getSpecTemplate().getTotalPointsSpent() 
				== Model.getProfile().getSpecTemplate().getMaxPoints()) {
			valid = false;
		}
		
		return valid;
	}
	
	/**
	 * Check if a decrease in points is possible.
	 * @return Decrease is possible.
	 */
	public final boolean isDecreasePossible() {
		boolean valid = true;
		
		if (this.points == 0) {
			valid = false;
		}
		
		/* If any talent in the tree requires points in this talent or 
		 * if any talent requires more than "treepoints - 1", then the 
		 * decrease is not possible. 
		 */
		for (Talent t 
				: Model.getProfile().getSpecTemplate()
				.getTree(this.tree).values()) {
			if (this == t.requiredTalent && t.points > 0
					&& t.requiredTalentPoints >= this.points - 1) {
				valid = false;
			}
			if (t != this && t.points > 0 && t.requiredTotalPoints 
					>= Model.getProfile().getSpecTemplate()
					.getTreePoints(this.tree) - 1) {
				valid = false;
			}
		}
		
		return valid;
	}

	/**
	 * (Try) to increase the points in this talent.
	 */
	public final void increase() {
		if (this.isIncreasePossible()) {
			this.points++;
			this.notifyModel();
		}
	}

	/**
	 * (Try) to decrease points in this talent.
	 */
	public final void decrease() {
		if (this.isDecreasePossible()) {
			this.points--;
			this.notifyModel();
		}
	}

	/**
	 * Set the points in this talent.
	 * @param i Points.
	 */
	public final void setPoints(final int i) {
		this.points = i;
		this.notifyModel();
	}
	
	/**
	 * Notify the parent model about the change.
	 */
	private void notifyModel() {
		Model.talentChanged(this);
	}
}
